#include <linux/init.h>
#include <linux/module.h>

#include <linux/fs.h>
// copy_to_user, copy_from_user 所在的函数库
#include <linux/uaccess.h>
// kmalloc 头文件
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/gfp.h>
// printk 日志是写在内核日志里面的，使用 dmesg 查看
unsigned char *kbuf;
static int driver_hello_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "driver_hello_open \n");
    int dlen = 1024*1024;
    kbuf = vmalloc_user(dlen);
    memset(kbuf, 0xDD, dlen);
    kbuf[dlen - 1] = 0xAA;
    kbuf[dlen - 2] = 0xBB;
    kbuf[dlen - 3] = 0xCC;
    kbuf[dlen - 4] = 0xEE;
    return 0;
}

static int driver_hello_release(struct inode *inode, struct file *file)
{
    vfree(kbuf);
    printk(KERN_INFO "driver_hello_release \n");
    return 0;
}

static ssize_t driver_hello_write(struct file *file, const char __user *ubuf,
                                  size_t count, loff_t *ppos)
{
    return 0;
}
ssize_t driver_hello_read(struct file *file, char __user *ubuf,
                          size_t count, loff_t *ppos)
{
    int ret = copy_to_user(ubuf, kbuf, count);
    if (ret)
    {
        printk(KERN_ERR "copy_from_user fail\n");
        return -EINVAL;
    }
    printk(KERN_INFO "copy_from_user success..\n");
    return count;
}
// file_operations 函数讲解。https://www.cnblogs.com/chen-farsight/p/6181341.html
static const struct file_operations driver_hello_ops =
    {
        .owner = THIS_MODULE,
        .open = driver_hello_open,
        .release = driver_hello_release,
        .write = driver_hello_write,
        .read = driver_hello_read,
};
#define MYNAME "driver_hello"
static int mymajor;
// __init关键字告诉链接器将代码放在内核对象文件中的专用部分中。
//本节事先对内核所知，并在模块加载和init函数完成后释放。这仅适用于内置驱动程序，不适用于可加载模块。内核将在启动序列期间首次运行驱动程序的init函数。
static int __init my_init(void)
{
    mymajor = register_chrdev(0, MYNAME, &driver_hello_ops);
    if (mymajor < 0)
    {
        printk(KERN_ERR "register_chrdev fail\n");
        return -EINVAL;
    }
    printk(KERN_INFO "register_chrdev success... mymajor = %d.\n", mymajor);
    return 0;
}

static void __exit my_exit(void)
{
    printk(KERN_INFO "chrdev_exit helloworld exit\n");

    // 在module_exit宏调用的函数中去注销字符设备驱动
    unregister_chrdev(mymajor, MYNAME);
    return;
}

module_init(my_init);
module_exit(my_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
// 描述模块的许可证
MODULE_LICENSE("GPL");
// 描述模块的作者
// MODULE_AUTHOR("aston");
// 描述模块的介绍信息
// MODULE_DESCRIPTION("module test");
// 描述模块的别名信息
// MODULE_ALIAS("alias xxx");